#include <iostream>
#include <dlfcn.h>
#include <vector>
#include <string>
#include "plugin_interface.h" //只编译好包含父类头文件，不需要包含子类的头文件

#define MTHOD0
//整体代码，除了动态库，都没有使用到涉及到子类，只使用了父类

int main() {
    // 1. 定义要加载的库路径
    std::vector<std::string> plugins = {
        "./plugins/add_plugin/libadd_plugin.so",
        "./plugins/multiply_plugin/libmultiply_plugin.so"   // 可以是绝对路径或相对路径
    };
    // plugin_path 库路径
    for (const auto& plugin_path : plugins) {
        // 2. 打开共享库；动态加载插件
        void* handle = dlopen(plugin_path.c_str(), RTLD_LAZY);//dlopen 加载插件库
        // void* dlopen(const char* filename, int flags);
        // flags: 加载标志，常用值：
        // RTLD_LAZY: 延迟绑定，只在需要时解析符号（推荐）
        // RTLD_NOW: 立即解析所有符号
        // RTLD_GLOBAL: 使库的符号对后续加载的库可见
        // RTLD_LOCAL: 符号只对本库可见（默认）
        if (!handle) {
            std::cerr << "Cannot load plugin: " << dlerror() << std::endl;
            continue;
        }
        // 3. 清除之前可能存在的错误，每次调用 dlopen 或 dlsym 后都应检查错误，dlerror() 会返回最近一次错误的字符串描述，调用 dlerror() 会清除错误状态
        dlerror();




        #ifdef MTHOD0
        /////////////// 实现调用
        // 写法1: 获取插件信息函数
        typedef PluginInterface* (*CreatePluginFunc)(); //定义一个PluginInterface类型的类指针，另起名称为CreatePluginFunc，参数为空
        CreatePluginFunc create_plugin = (CreatePluginFunc)dlsym(handle, "create_plugin");//在库中查找函数create_plugin返回void*类型，转换为CreatePluginFunc类型
        if (!create_plugin) { //未获取到函数就报错
            std::cerr << "无法获取插件创建函数: " << dlerror() << std::endl;
            dlclose(handle);
            return 1;
        }
        // 创建插件实例
        PluginInterface* plugin = create_plugin(); //将获取到的插件类实例化，若此类为AddPlugin，则plugin为AddPlugin的实例
        if (!plugin) {
            std::cerr << "无法创建插件实例" << std::endl;
            dlclose(handle);
            return 1;
        }
        // 调用插件功能
        std::cout << "插件名称: " << plugin->get_name() << std::endl;
        std::cout << "1 + 2 = "  << plugin->operate(1,2) << std::endl;
        plugin->log("hello");
        delete plugin;
        dlclose(handle);
        #endif

        // 如果插件库返回的是对象（如 PluginInterface 而非 PluginInterface*），你的代码会出错。针对上一次提交进行更改

        #ifdef MTHOD1
        // 如果插件库返回的是对象（如 PluginInterface 而非 PluginInterface*），你的代码会出错。针对上一次提交进行更改
        auto get_plugin = reinterpret_cast<PluginInterface*(*)()>(dlsym(handle, "create_plugin"));//dlsym 获取 create_plugin 函数
        // 如何是函数可以运行; 如果插件库返回的是对象（如 PluginInterface 而非 PluginInterface*），以下代码会出错。
        // auto get_plugin = reinterpret_cast<PluginInterface(*)()>(dlsym(handle, "create_plugin"));//dlsym 获取 create_plugin 函数
        // void* dlsym(void* handle, const char* symbol);
        // handle: dlopen 返回的句柄
        // symbol: 要查找的符号名称（函数或变量名）
        // 返回符号地址，需要转换为正确的类型
        // 将找到的符号地址（void* 类型）转换为 PluginInfo(*)() 类型的无参数的函数指针，PluginInfo() 是一个函数
        if (!get_plugin) { //成功获取到插件的子函数或其指针
            std::cerr << "Cannot load symbol: " << dlerror() << std::endl;
            dlclose(handle);
            continue;
        }

        // // 创建插件实例， 插件子函数返回一个父类类型的指针， 实例化为对象
        PluginInterface* info = get_plugin(); 
        if (!info) {
            std::cerr << "Failed to create plugin instance" << std::endl;
            dlclose(handle);
            return 1;
        }
        // 使用插件功能
        int result = info->operate(5, 3);  //看似是父类的方法，起始调用的是子类的方法，多态的特性
        std::cout << "Plugin: " << info->get_name() 
                  << ", Result: " << result << std::endl;

        // 关闭插件
        dlclose(handle);
        #endif
    }

    return 0;
}